package common

import (
	"fmt"
	"strings"
	"time"
)

// 实体字段类型。
type DbFieldType int8

// 命令参数类型。
type DbParameterType int8

// 命令参数的匹配方式。
type DbParameterMatch int8

// 命令结果类型。
type DbResultType int8

const (
	// DbFieldType
	FtString   DbFieldType = 1
	FtInteger  DbFieldType = 2
	FtDecimal  DbFieldType = 3
	FtDouble   DbFieldType = 4
	FtDateTime DbFieldType = 5
	FtBoolean  DbFieldType = 6
	FtBinary   DbFieldType = 7
	FtEnum     DbFieldType = 8

	// DbResultType
	RtEntities DbResultType = 1
	RtEntity   DbResultType = 2
	RtString   DbResultType = 3
	RtInteger  DbResultType = 4
	RtDecimal  DbResultType = 5
	RtDouble   DbResultType = 6
	RtDateTime DbResultType = 7
	RtBoolean  DbResultType = 8
	RtBinary   DbResultType = 9
	RtObjects  DbResultType = 10

	RtStringList   DbResultType = 11
	RtIntegerList  DbResultType = 12
	RtDecimalList  DbResultType = 13
	RtDoubleList   DbResultType = 14
	RtDateTimeList DbResultType = 15
	RtBooleanList  DbResultType = 16
	RtBinaryList   DbResultType = 17
	RtObjectsList  DbResultType = 18

	RtUpdatedCount DbResultType = 19

	// DbParameterType
	PtString     DbParameterType = 1
	PtInteger    DbParameterType = 2
	PtDecimal    DbParameterType = 3
	PtDouble     DbParameterType = 4
	PtDateTime   DbParameterType = 5
	PtBoolean    DbParameterType = 6
	PtBinary     DbParameterType = 7
	PtIntegerSet DbParameterType = 8
	PtStringSet  DbParameterType = 9

	// DbParameterMatch
	PmExact      DbParameterMatch = 1
	PmStartsWith DbParameterMatch = 2
	PmEndsWith   DbParameterMatch = 3
	PmContains   DbParameterMatch = 4
)

func (self DbFieldType) String() string {
	switch self {
	case FtString:
		return "STRING"
	case FtInteger:
		return "INTEGER"
	case FtDecimal:
		return "DECIMAL"
	case FtDouble:
		return "DOUBLE"
	case FtDateTime:
		return "DATETIME"
	case FtBoolean:
		return "BOOLEAN"
	case FtBinary:
		return "BINARY"
	default:
		return fmt.Sprintf("UNKNOWN(%d)", int(self))
	}
}

func (self DbResultType) String() string {
	switch self {
	case RtEntities:
		return "ENTITIES"
	case RtEntity:
		return "ENTITY"
	case RtString:
		return "STRING"
	case RtInteger:
		return "INTEGER"
	case RtDecimal:
		return "DECIMAL"
	case RtDouble:
		return "DOUBLE"
	case RtDateTime:
		return "DATETIME"
	case RtBoolean:
		return "BOOLEAN"
	case RtBinary:
		return "BINARY"
	case RtObjects:
		return "OBJECTS"
	case RtStringList:
		return "STRING_LIST"
	case RtIntegerList:
		return "INTEGER_LIST"
	case RtDecimalList:
		return "DECIMAL_LIST"
	case RtDoubleList:
		return "DOUBLE_LIST"
	case RtDateTimeList:
		return "DATETIME_LIST"
	case RtBooleanList:
		return "BOOLEAN_LIST"
	case RtBinaryList:
		return "BINARY_LIST"
	case RtObjectsList:
		return "OBJECTS_LIST"
	case RtUpdatedCount:
		return "UPDATED_COUNT"
	default:
		return fmt.Sprintf("UNKNOWN(%d)", int(self))
	}
}

func (self DbParameterType) String() string {
	switch self {
	case PtString:
		return "STRING"
	case PtInteger:
		return "INTEGER"
	case PtDecimal:
		return "DECIMAL"
	case PtDouble:
		return "DOUBLE"
	case PtDateTime:
		return "DATETIME"
	case PtBoolean:
		return "BOOLEAN"
	case PtBinary:
		return "BINARY"
	case PtIntegerSet:
		return "INTEGER_SET"
	case PtStringSet:
		return "STRING_SET"
	default:
		return fmt.Sprintf("UNKNOWN(%d)", int(self))
	}
}

func (self DbParameterMatch) String() string {
	switch self {
	case PmExact:
		return "EXACT"
	case PmStartsWith:
		return "STARTS_WITH"
	case PmEndsWith:
		return "ENDS_WITH"
	case PmContains:
		return "CONTAINS"
	default:
		return fmt.Sprintf("UNKNOWN(%d)", int(self))
	}
}

// LineInfo 表示源文件的位置。
type LineInfo struct {
	FileName   string
	LineNumber int
}

func (self LineInfo) String() string {
	if self.LineNumber > 0 {
		return fmt.Sprintf("%s:%d", self.FileName, self.LineNumber)
	} else {
		return fmt.Sprintf("%s:??", self.FileName)
	}
}

// 表示代码结构中的一个对象。
type DbObject interface {
	// 获取代码结构的名字，比如 entity, module, statement 等等。
	elementType() string
}

// 表示可以包含innerText的对象。
type DbMixed interface {
	AddText(s string)
}

// 根元素对象。
type DbRoot struct {
	// 来源文件中的位置。
	LineInfo
	// 包名。
	Package string
	// 包的描述。
	Description string
	// 来源文件的时间戳。
	Timestamp time.Time
}

func (self *DbRoot) elementType() string {
	return "root"
}

// 实体对象。
type DbEntity struct {
	// 来源文件中的位置。
	LineInfo
	// 包名。
	Package string
	// 实体名。
	Name string
	// 实体的描述。
	Description string
	// 来源文件的时间戳。
	Timestamp time.Time
	// 实体包含的字段列表。
	Fields []*DbField
}

func (self *DbEntity) elementType() string {
	return "entity"
}

func (self *DbEntity) getFullName() string {
	if self.Package != "" {
		return self.Package + "." + self.Name
	} else {
		return self.Name
	}
}

func (self *DbEntity) AddField(f *DbField) {
	if f == nil {
		panic("Argument \"field\" must not be nil")
	}

	for _, item := range self.Fields {
		if item.Name == f.Name {
			panic(fmt.Sprintf("Duplicated field %q of %q", f.Name, self.Name))
		}
	}

	self.Fields = append(self.Fields, f)
}

func (self *DbEntity) String() string {
	result := fmt.Sprintf("Entity{Name=%s,Description=%q", self.getFullName(), self.Description)
	if len(self.Fields) > 0 {
		result += ",\n"
		for _, field := range self.Fields {
			result += fmt.Sprintf("  %v\n", field)
		}
	}
	result += "}"

	return result
}

// 实体字段。
type DbField struct {
	// 来源文件中的位置。
	LineInfo
	// 字段名。
	Name string
	// 字段的描述。
	Description string
	// 字段的类型。
	Type DbFieldType
	// 枚举类型的完整类型名。
	EnumType string
	// 字段的默认值。
	DefaultValue string
	// 是否关键字段。
	Key bool
}

func (self *DbField) elementType() string {
	return "field"
}

func (self *DbField) String() string {
	result := fmt.Sprintf("%s %s", self.Name, self.Type)
	if self.DefaultValue != "" {
		result = result + " DEFAULT " + self.DefaultValue
	}
	if self.Key {
		result = result + " PRIMARY KEY"
	}
	if self.Description != "" {
		result = result + " " + fmt.Sprintf("%q", self.Description)
	}

	return result
}

// 数据访问模块。
type DbModule struct {
	// 来源文件中的位置。
	LineInfo
	// 包名。
	Package string
	// 模块名。
	Name string
	// 模块的描述。
	Description string
	// 来源文件的时间戳。
	Timestamp time.Time
	// 预定义SQL语句列表。
	Sqls []*DbSql
	// 命令列表。
	Commands []DbCommand
}

func (self *DbModule) elementType() string {
	return "module"
}

func (self *DbModule) getFullName() string {
	if self.Package != "" {
		return self.Package + "." + self.Name
	} else {
		return self.Name
	}
}

func (self *DbModule) AddSql(s *DbSql) {
	if s == nil {
		panic("Argument \"sql\" must not be nil")
	}

	for _, item := range self.Sqls {
		if item.Name == s.Name {
			panic(fmt.Sprintf("Duplicated sql %q of %q", s.Name, self.Name))
		}
	}

	self.Sqls = append(self.Sqls, s)
}

func (self *DbModule) AddCommand(c DbCommand) {
	if c == nil {
		panic("Argument \"command\" must not be nil")
	}

	for _, item := range self.Commands {
		if item.getName() == c.getName() {
			panic(fmt.Sprintf("Duplicated command %q of %q", c.getName(), self.Name))
		}
	}

	self.Commands = append(self.Commands, c)
}

func (self *DbModule) String() string {
	result := fmt.Sprintf("Module{Name=%s,Description=%q", self.getFullName(), self.Description)
	if len(self.Sqls) > 0 || len(self.Commands) > 0 {
		result += ",\n"
		for _, sql := range self.Sqls {
			result += fmt.Sprintf("  %v,\n", sql)
		}
		for _, command := range self.Commands {
			result += fmt.Sprintf("  %v,\n", command)
		}
	}
	result += "}"

	return result
}

// 预定义SQL。
type DbSql struct {
	// 来源文件中的位置。
	LineInfo
	// 预定义名。
	Name string
	// SQL内容。
	Content string
}

func (self *DbSql) elementType() string {
	return "sql"
}

func (self *DbSql) String() string {
	return fmt.Sprintf("Sql{Name=%s,Content=%q}", self.Name, self.Content)
}

func (self *DbSql) AddText(s string) {
	s = strings.TrimSpace(s)
	if s != "" {
		if len(self.Content) > 0 {
			self.Content = self.Content + "\n" + s
		} else {
			self.Content = s
		}
	}
}

type DbCommand interface {
	DbObject
	// 获取命令的名字。
	getName() string
	// 添加一个参数。
	AddParameter(parameter *DbParameter)
}

type DbStatement struct {
	// 来源文件中的位置。
	LineInfo
	Name        string
	Description string
	ResultType  DbResultType
	EntityName  string
	ReadOnly    bool
	AutoKey     string
	Texts       []DbText
	Parameters  []*DbParameter
}

func (self *DbStatement) elementType() string {
	return "statement"
}

func (self *DbStatement) getName() string {
	return self.Name
}

func (self *DbStatement) AddText(s string) {
	s = strings.TrimSpace(s)
	if s != "" {
		self.Texts = append(self.Texts, &DbStaticText{Content: s})
	}
}

func (self *DbStatement) AddSqlRef(sqlRef *DbSqlRef) {
	if sqlRef == nil {
		panic("Argument \"sqlRef\" must not be nil")
	}

	for _, item := range self.Texts {
		if item == sqlRef {
			panic(fmt.Sprintf("Duplicated sqlRef %q of %q", sqlRef, self.Name))
		}
	}

	self.Texts = append(self.Texts, sqlRef)
}

func (self *DbStatement) AddDynamicText(dynamicText *DbDynamicText) {
	if dynamicText == nil {
		panic("Argument \"dynamicText\" must not be nil")
	}

	self.Texts = append(self.Texts, dynamicText)
}

func (self *DbStatement) AddParameter(p *DbParameter) {
	if p == nil {
		panic("Argument \"parameter\" must not be nil")
	}

	for _, item := range self.Parameters {
		if item.Name == p.Name {
			panic(fmt.Sprintf("Duplicated parameter %q of %q", p.Name, self.Name))
		}
	}

	self.Parameters = append(self.Parameters, p)
}

func (self *DbStatement) String() string {
	result := fmt.Sprintf("Statement{Name=%s,ResultType=%s,EntityName=%q,ReadOnly=%t,AutoKey=%s,Description=%q", self.Name, self.ResultType, self.EntityName, self.ReadOnly, self.AutoKey, self.Description)

	if len(self.Texts) > 0 || len(self.Parameters) > 0 {
		result += ",\n"
		for _, text := range self.Texts {
			result += fmt.Sprintf("  %v,\n", text)
		}
		for _, parameter := range self.Parameters {
			result += fmt.Sprintf("  %v,\n", parameter)
		}
	}
	result += "}"

	return result
}

type DbProcedure struct {
	// 来源文件中的位置。
	LineInfo
	// 存储过程名。
	Name string
	// 存储过程描述。
	Description string
	// 数据库中的存储过程名。
	Text string
	// 返回值类型。
	ResultType DbResultType
	// 返回实体的名字。
	EntityName string
	// 是否只读。
	ReadOnly bool
	// 存储过程使用的参数列表。
	Parameters []*DbParameter
}

func (self *DbProcedure) elementType() string {
	return "statement"
}

func (self *DbProcedure) getName() string {
	return self.Name
}

func (self *DbProcedure) AddParameter(p *DbParameter) {
	if p == nil {
		panic("Argument \"parameter\" must not be nil")
	}

	for _, item := range self.Parameters {
		if item.Name == p.Name {
			panic(fmt.Sprintf("Duplicated parameter %q of %q", p.Name, self.Name))
		}
	}

	self.Parameters = append(self.Parameters, p)
}

func (self *DbProcedure) String() string {
	result := fmt.Sprintf("Procedure{Name=%s,Text=%q,ResultType=%s,EntityName=%q,ReadOnly=%t,Description=%q", self.Name, self.Text, self.ResultType, self.EntityName, self.ReadOnly, self.Description)

	if len(self.Parameters) > 0 {
		result += ",\n"
		for _, parameter := range self.Parameters {
			result += fmt.Sprintf("  %v,\n", parameter)
		}
	}
	result += "}"

	return result
}

type DbParameter struct {
	// 来源文件中的位置。
	LineInfo
	// 参数的名字。
	Name string
	// 参数的描述。
	Description string
	// 参数的类型。
	Type DbParameterType
	// 参数的枚举类型名，当 Type 字段的值是 PtEnum 时有效。
	EnumType string
	// 参数的长度，当 Type 字段的值是 PtString 或者 PtBinary 时有效。
	Size int16
	// 参数的匹配方式，当 Type 字段的值是 PtString 时有效。
	Match DbParameterMatch
}

func (self *DbParameter) elementType() string {
	return "parameter"
}

func (self *DbParameter) getFullType() string {
	if self.Size > 0 {
		return fmt.Sprintf("%s(%d)", self.Type, self.Size)
	} else {
		return fmt.Sprintf("%s", self.Type)
	}
}

func (self *DbParameter) String() string {
	return fmt.Sprintf("Parameter{Name=%s,Type=%s,Match=%s,Description=%q}", self.Name, self.getFullType(), self.Match, self.Description)
}

type DbText interface {
	DbObject
}

type DbStaticText struct {
	// 来源文件中的位置。
	LineInfo
	// 静态SQL文本。
	Content string
}

func (self *DbStaticText) elementType() string {
	return "#CDATA#"
}

func (self *DbStaticText) String() string {
	return fmt.Sprintf("StaticText{%q}", self.Content)
}

type DbSqlRef struct {
	// 来源文件中的位置。
	LineInfo
	// 引用的预定义SQL文本的名字。
	Name string
}

func (self *DbSqlRef) elementType() string {
	return "sqlRef"
}

func (self *DbSqlRef) String() string {
	return fmt.Sprintf("SqlRef{Name=%s}", self.Name)
}

// 动态SQL文本。
type DbDynamicText struct {
	// 来源文件中的位置。
	LineInfo
	// 动态文本的前缀。
	Prepend string
	// 动态文本的后缀
	Append string
	// 动态文本的连接符
	Joint string
	// 动态文本包含的所有块列表
	Chunks []*DbDynamicChunk
}

func (self *DbDynamicText) elementType() string {
	return "dynamic"
}

func (self *DbDynamicText) AddChunk(chunk *DbDynamicChunk) {
	if chunk == nil {
		panic("Argument \"chunk\" must not be nil")
	}

	for _, item := range self.Chunks {
		if item == chunk {
			panic(fmt.Sprintf("Duplicated chunk %v of %v", chunk, self))
		}
	}

	self.Chunks = append(self.Chunks, chunk)
}

func (self *DbDynamicText) String() string {
	result := fmt.Sprintf("DynamicText{Prepend=%q,Joint=%q,Append=%q", self.Prepend, self.Joint, self.Append)

	if len(self.Chunks) > 0 {
		result += ",\n"
		for _, chunk := range self.Chunks {
			result += fmt.Sprintf("  %v,\n", chunk)
		}
	}
	result += "}"

	return result
}

type DbDynamicChunk struct {
	// 来源文件中的位置。
	LineInfo
	// 动态块的名字
	Name string
	// 动态块检测的参数名
	Parameter string
	// 动态块检测参数时使用的值
	Value string
	// 动态块包含的内容
	Content string
}

func (self *DbDynamicChunk) elementType() string {
	return self.Name
}

func (self *DbDynamicChunk) AddText(s string) {
	s = strings.TrimSpace(s)
	if s != "" {
		if len(self.Content) > 0 {
			self.Content = self.Content + "\n" + s
		} else {
			self.Content = s
		}
	}
}

func (self *DbDynamicChunk) String() string {
	return fmt.Sprintf("%s{Parameter=%s,Value=%q,Content=%q}", self.Name, self.Parameter, self.Value, self.Content)
}
